﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Ue.Infrastructure.Caching;
using Ue.Infrastructure.Caching.VolatileProvider;

namespace Ue.Tests.Infrastructure.Caching.VolatileProvider
{
    [TestClass]
    public class ClockTests
    {
        ICacheManager cacheManager;
        StubClock clock;

        [TestInitialize]
        public void Initial()
        {
            this.cacheManager = new CacheManager(typeof(CacheTests), new CacheHolder(new CacheContextAccessor()));
            this.clock = new StubClock();
        }

        [TestMethod]
        public void ShouldHandleAbsoluteTime()
        {
            var inOneSecond = clock.UtcNow.AddSeconds(1);
            int cached = 0;

            Func<int> retrieve = () =>
            {
                return this.cacheManager.Get("testItem", ctx =>
                {
                    ctx.Monitor(clock.WhenUtc(inOneSecond));
                    return ++cached;
                });
            };

            ///第一次执行，把对象加入缓存中，并返回对象
            Assert.AreEqual(1, retrieve());

            for (int i = 0; i < 10; i++)
            {
                Assert.AreEqual(retrieve(), 1);
            }

            this.clock.Advance(TimeSpan.FromSeconds(1));

            Assert.AreEqual(2, retrieve());
            Assert.AreEqual(3, retrieve());
            Assert.AreEqual(4, retrieve());
        }

        [TestMethod]
        public void ShouldHandleAbsoluteTimeSpan()
        {
            int cached = 0;

            Func<int> retrieve = () =>
            {
                return this.cacheManager.Get("testItem", ctx =>
                {
                    ctx.Monitor(clock.When(TimeSpan.FromSeconds(1)));
                    return ++cached;
                });
            };

            ///第一次执行，把对象加入缓存中，并返回对象
            Assert.AreEqual(1, retrieve());

            for (int i = 0; i < 10; i++)
            {
                Assert.AreEqual(retrieve(), 1);
            }

            this.clock.Advance(TimeSpan.FromSeconds(1));
            for (int i = 0; i < 10; i++)
            {
                Assert.AreEqual(retrieve(), 2);
            }

            this.clock.Advance(TimeSpan.FromSeconds(1));
            for (int i = 0; i < 10; i++)
            {
                Assert.AreEqual(retrieve(), 3);
            }
        }
    }

    public class StubClock : IClock
    {
        public StubClock()
        {
            UtcNow = new DateTime(2009, 10, 14, 12, 34, 56, DateTimeKind.Utc);
        }

        public DateTime UtcNow { get; private set; }

        public void Advance(TimeSpan span)
        {
            UtcNow = UtcNow.Add(span);
        }

        public DateTime FutureMoment(TimeSpan span)
        {
            return UtcNow.Add(span);
        }


        public IVolatileToken When(TimeSpan duration)
        {
            return new Clock.AbsoluteExpirationToken(this, duration);
        }

        public IVolatileToken WhenUtc(DateTime absoluteUtc)
        {
            return new Clock.AbsoluteExpirationToken(this, absoluteUtc);
        }
    }
}
